package com.Graphics.OGL.Sphere.SphereRenderer;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.util.Log;

import com.Graphics.OGL.Sphere.R;

//import android.opengl.GLES20;

public class SphereRenderer implements Renderer
{
	Bitmap m_texBitmap;  //The bitmap for the texture.
	int[] m_texture = new int[1];   //The OpenGL texture ID.
	float m_angle = 0.0f;
	float m_lightAngle = 0.0f;
	// set flag for lignt
	public boolean lightOn = true;
	
	/* The earth sphere drawing resolution. */
	final int NUMOFLATITUDE = 36;
	final int NUMOFLONGITUDE = 72;
	
	private FloatBuffer m_vertexBuffer;
	private FloatBuffer m_texBuffer;
	
	public SphereRenderer(Context ctx)
	{
		m_texBitmap = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.earth_2);
		
		allocBuffer();
		initVertexData();
	}
	
	protected void initTexture(GL10 gl)
	{
		//Generate a texture;
		gl.glGenTextures(1, m_texture, 0);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
				GL10.GL_LINEAR);
		//Bind a texture;
		gl.glBindTexture(GL10.GL_TEXTURE_2D, m_texture[0]);
		//Load the texture;
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, m_texBitmap, 0);
		//Set the filtering mode.
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
				GL10.GL_LINEAR);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
				GL10.GL_LINEAR);
	}
	
	protected void initLight0(GL10 gl)
	{
		float ambientLight[] = {0.1f,0.1f,0.1f,1.0f};
		gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT,ambientLight,0);
		float diffuseLight[]={0.6f,0.6f,0.6f,1.0f};
		gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE,diffuseLight,0);
		float specular []={1.0f,1.0f,1.0f,1.0f};
		gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_SPECULAR,specular,0);
		float[] lightPos={10.0f,0.0f,10.0f,1.0f};
		gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_POSITION,lightPos,0);
		
		gl.glEnable(GL10.GL_LIGHT0);
	}
	
	protected void initMaterial(GL10 gl)
	{
		gl.glEnable(GL10.GL_COLOR_MATERIAL);
	
		float []ambientMaterial = {0.4f,0.4f,0.4f,1.0f};
		gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_AMBIENT,ambientMaterial,0);
		float []diffuseMaterial = {0.8f,0.8f,0.8f,1.0f};
		gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_DIFFUSE,diffuseMaterial,0);
		float []specularMaterial ={1.0f,1.0f,1.0f,1.0f};
		gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SPECULAR,specularMaterial,0);
		float []shineMaterial={0.5f};
		gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SHININESS, shineMaterial,0);
	}
	
	protected void allocBuffer()
	{
		ByteBuffer vbb = ByteBuffer.allocateDirect(NUMOFLONGITUDE * NUMOFLATITUDE * 2 * 3 * (Float.SIZE / 8));
		vbb.order(ByteOrder.nativeOrder());
		m_vertexBuffer = vbb.asFloatBuffer();
		m_vertexBuffer.position(0);
		
		ByteBuffer tbb = ByteBuffer.allocateDirect(NUMOFLONGITUDE * NUMOFLATITUDE * 2 * 2 * (Float.SIZE / 8));
		tbb.order(ByteOrder.nativeOrder());
		m_texBuffer = tbb.asFloatBuffer();
		m_texBuffer.position(0);
	}

    /***********************************************************
     * Init vertex data of the earth sphere, including position and texture
     * coordinates.
     */
    protected void initVertexData()
    {
        final float DE2RA   = 0.01745329252f;   // coefficient when convert from degree to radian.
        final float RA2DE   = 57.2957795129f;   // coefficient when convert from radian to degree.
        final float PIOVER2 = 1.570796326795f;  // PI / 2

        float start_lat     = -90.0f;
        float start_lon     = 0.0f;
        float Rad = 1.0f;

        float lat_incr = 180.0f / NUMOFLATITUDE;
        float lon_incr = 360.0f / NUMOFLONGITUDE;

        int row, col;

        float phi1, phi2;
        float theta1;

        /* Initial the current position of data buffer to zero. */
        m_vertexBuffer.position(0);
        m_texBuffer.position(0);

        for (col = 0; col < NUMOFLONGITUDE; col++)
        {
            phi1 = (start_lon + col * lon_incr) * DE2RA;
            phi2 = (start_lon + (col + 1) * lon_incr) * DE2RA;

            for (row = 0; row < NUMOFLATITUDE; row++)
            {
                theta1 = (start_lat + row * lat_incr) * DE2RA;

                // vertex 1
                // x
                m_vertexBuffer.put((float) (Rad * Math.cos(phi1) * Math.cos(theta1)));
                // y
                m_vertexBuffer.put((float) (Rad * Math.sin(theta1)));
                // z
                m_vertexBuffer.put((float) (Rad * Math.sin(phi1) * Math.cos(theta1)));

                // vertex 2
                // x
                m_vertexBuffer.put((float) (Rad * Math.cos(phi2) * Math.cos(theta1)));
                // y
                m_vertexBuffer.put((float) (Rad * Math.sin(theta1)));
                // z
                m_vertexBuffer.put((float) (Rad * Math.sin(phi2) * Math.cos(theta1)));

                // Tex coord1
                // x
                m_texBuffer.put((phi1 * RA2DE) / 360.0f);
                // y
                m_texBuffer.put((theta1 + PIOVER2) * RA2DE / 180.0f);

                // Tex coord2
                // x
                m_texBuffer.put((phi2 * RA2DE) / 360.0f);
                // y
                m_texBuffer.put((theta1 + PIOVER2) * RA2DE / 180.0f);
            }
        }

        /* Set the current position of data buffer to zero. */
        m_vertexBuffer.position(0);
        m_texBuffer.position(0);
    }

	@Override
	public void onDrawFrame(GL10 gl)
	{
		// TODO Auto-generated method stub
		
		Log.i("Jerry", "onDrawFrame!");
		
		gl.glPushMatrix();   //Save the matrix state.

		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		
		gl.glEnable(GL10.GL_DEPTH_TEST);
		gl.glEnable(GL10.GL_TEXTURE_2D);
		//open the light
		if(lightOn)
		{
			gl.glPushMatrix();
			gl.glRotatef(m_lightAngle, 0.0f, 1.0f, 0.0f);
			m_lightAngle += 2.0f;
			if(m_lightAngle > 360)
			{
				m_lightAngle -=360;
			}

		    initLight0(gl);
		    gl.glEnable(GL10.GL_LIGHTING);
		    gl.glPopMatrix();
		}
		else
		{
			gl.glDisable(GL10.GL_LIGHTING);
		}
		
		gl.glPushMatrix();
		
		gl.glRotatef(m_angle, 0.0f, 1.0f, 0.0f);
		m_angle -= 1.0f;
		if(m_angle < 0.0f)
		{
			m_angle += 360.0f;
		}

		gl.glBindTexture(GL10.GL_TEXTURE_2D, m_texture[0]);

		/*Specify the vertex data.*/
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, m_vertexBuffer);
		gl.glNormalPointer(GL10.GL_FLOAT, 0, m_vertexBuffer);  //The normal vector are equal to vertex pos.
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, m_texBuffer);
		
		for(int col = 0; col < NUMOFLONGITUDE; col++)
		{
			gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, col * NUMOFLATITUDE * 2, NUMOFLATITUDE * 2);
		}
		
		gl.glPopMatrix();
		
		gl.glFinish();
		
		gl.glDisable(GL10.GL_LIGHTING);
		
		gl.glPopMatrix();     //Restore matrix state.
	}

	@Override
	public void onSurfaceChanged(GL10 arg0, int arg1, int arg2)
	{
		// TODO Auto-generated method stub
		arg0.glViewport(0, 0, arg1, arg2);
		float ratio = (float)arg1/arg2;
		arg0.glMatrixMode(GL10.GL_PROJECTION);
		arg0.glLoadIdentity();
		arg0.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
		
		arg0.glMatrixMode(GL10.GL_MODELVIEW);
        arg0.glLoadIdentity();
        GLU.gluLookAt(arg0, 0, 0, 5, 0f, 0f, 0f, 0f, -1.0f, 0.0f);
	}

	@Override
	public void onSurfaceCreated(GL10 arg0, EGLConfig arg1)
	{
		Log.i("Eva","open the light");
		// TODO Auto-generated method stub
		arg0.glDisable(GL10.GL_DITHER);
		arg0.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
		arg0.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		arg0.glShadeModel(GL10.GL_SMOOTH);
		initMaterial(arg0);
		
		arg0.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		arg0.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		arg0.glEnableClientState(GL10.GL_NORMAL_ARRAY);
		
		initTexture(arg0);
	}	
}
